package cn.lxb.netstatagent;

import java.net.SocketAddress;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing;

/**
 * TODO: 把地址列表放在线程变量中,新增的才写文件,但是虚拟线程没办法.
 * @author Administrator
 *
 */
public class SocketAdvice {

	@Advice.OnMethodEnter
	public static void enter(@Advice.Argument(0) SocketAddress address) {
		try {
			StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			boolean debug = System.getProperty("netstat-agent.debug", "false").equals("true");
			for (StackTraceElement element : stackTrace) {
				if ("java.nio.channels.Selector".equals(element.getClassName())
						&& "open".equals(element.getMethodName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是NIO Selector.open调用,非真正的网络请求: " + address);
					}
					return;
				}

				if ("io.netty.channel.nio.NioEventLoop".equals(element.getClassName())
						&& "openSelector".equals(element.getMethodName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是netty NioEventLoop.openSelector调用,非真正的网络请求: " + address);
					}
					return;
				}
				
				if ("cn.lxb.netstatagent.AddressRecordService".equals(element.getClassName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是agent自身调用,不需要记录: " + address);
					}
					return;
				}
			}
			String addr = address.toString();
			int idx = addr.indexOf('/');
			if (idx > 0) {
				addr = addr.substring(0, idx) + addr.substring(addr.lastIndexOf(':'));
			} else if (idx == 0) {
				addr = addr.substring(idx+1);
			}
			System.out.println("NA-DEBUG: 目标地址:" + address+",处理后:"+addr);
			
			Class<?> addressRecorderClass=Thread.currentThread().getContextClassLoader().loadClass("cn.lxb.netstatagent.AddressRecordService");
			addressRecorderClass.getMethod("record", String.class).invoke(null, addr);
			
		} catch (Exception e) {
			// 吃掉一切异常,不能影响原代码.
			e.printStackTrace();
		}
	}

	/**
	 * 不能调用非JDK的方法,包括旁边的,可能是类加载器的原因. 返回值: @Advice.Return(readOnly = false, typing =
	 * Typing.DYNAMIC) Object returnValue 异常: @Advice.Thrown Throwable throwable
	 */
//		@Advice.OnMethodExit(onThrowable = Throwable.class)
	public static void exit(@Advice.Argument(0) SocketAddress address,
			@Advice.Return(readOnly = false, typing = Typing.DYNAMIC) Object returnValue,
			@Advice.Thrown Throwable throwable) {
		try {
			boolean debug = System.getProperty("netstat-agent.debug", "false").equals("true");
			StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			for (StackTraceElement element : stackTrace) {
				if ("java.nio.channels.Selector".equals(element.getClassName())
						&& "open".equals(element.getMethodName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是NIO Selector.open调用,非真正的网络请求: " + address);
					}
					return;
				}

				if ("io.netty.channel.nio.NioEventLoop".equals(element.getClassName())
						&& "openSelector".equals(element.getMethodName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是netty NioEventLoop.openSelector调用,非真正的网络请求: " + address);
					}
					return;
				}
				
				if ("cn.lxb.netstatagent.AddressRecordService".equals(element.getClassName())) {
					if(debug) {
						System.out.println("NA-DEBUG: 这是agent自身调用,不需要记录: " + address);
					}
					return;
				}
			}
			
			// .... TODO not complete
		
		} catch (Exception e) {
			// 吃掉一切异常,不能影响原代码.
			e.printStackTrace();
		}

	}
	
}
